package com.example.literj.kt.android.schedulers

import android.os.Handler
import android.os.Message
import com.example.literj.kt.core.Scheduler
import com.example.literj.kt.disposables.Disposable
import com.example.literj.kt.plugins.RxJavaPlugins
import java.util.concurrent.TimeUnit

class HandlerScheduler(private val handler: Handler, private val async: Boolean) : Scheduler() {
    override fun scheduleDirect(run: Runnable?, delay: Long, unit: TimeUnit?): Disposable {
        if (run == null) throw NullPointerException("run == null")
        if (unit == null) throw NullPointerException("unit == null")

        val runnable = RxJavaPlugins.onSchedule(run)
        val scheduled = ScheduledRunnable(handler, runnable)
        val message = Message.obtain(handler, scheduled)
        if (async) {
            message.isAsynchronous = true
        }
        handler.sendMessageDelayed(message, unit.toMillis(delay))
        return scheduled
    }

    override fun createWorker() = HandlerWorker(handler, async)

    class HandlerWorker internal constructor(
        private val handler: Handler,
        private val async: Boolean
    ) : Worker() {
        @Volatile
        private var disposed = false

        override fun schedule(run: Runnable?, delay: Long, unit: TimeUnit?): Disposable {
            if (run == null) throw NullPointerException("run == null")
            if (unit == null) throw NullPointerException("unit == null")

            if (disposed) {
                return Disposable.disposed()
            }

            val runnable = RxJavaPlugins.onSchedule(run)
            val scheduled = ScheduledRunnable(handler, runnable)
            val message = Message.obtain(handler, scheduled)
            message.obj = this
            if (async) {
                message.isAsynchronous = true
            }
            handler.sendMessageDelayed(message, unit.toMillis(delay))
            if (disposed) {
                handler.removeCallbacks(scheduled)
                return Disposable.disposed()
            }
            return scheduled
        }

        override fun dispose() {
            disposed = true
            handler.removeCallbacksAndMessages(this)
        }

        override fun isDisposed(): Boolean {
            return disposed
        }
    }

    private class ScheduledRunnable internal constructor(
        private val handler: Handler,
        private val delegate: Runnable?
    ) : Runnable, Disposable {
        @Volatile
        var disposed = false

        override fun run() {
            try {
                delegate?.run()
            } catch (t: Throwable) {
                RxJavaPlugins.onError(t)
            }
        }

        override fun dispose() {
            handler.removeCallbacks(this)
            disposed = true
        }

        override fun isDisposed() = disposed
    }
}